/*User program on Nachos
  For project 4
*/

/*Author: Yuzhe Wu 4/14*/

#include "create.h"
void nurseTaskDelay(){
	int r;
	int i;
	r = Rand(256)+50;
	for(i=0;i<r;i++){
		Yield();	
	}
}

void main () {
	int myERID = -1;	/*the empty exam room I find to bring patient to when patient in waiting room, or xray*/
	int myDocID = -1;/*the free doctor I find when patient in exam room*/
	int myXRID = -1; 
	int myWallPocketERID = -1;	/*the exam room No. which has a filled wall pocket*/
	int myWallPocketXRID = -1;	/*the eray room No. which has a filled wall pocket*/
	int *tmpNurseID = NULL;
	
	struct examSheet myExamSheet;
	
	int id = -1;
	bool debugFlag = true;

	/*Printf("P4DEBUG: I am nurse, in!"\n);*/
	/*Get index*/
	doCreate();
	
	rpcAcquireLock(nurseIndexLock);
	id=rpcGetValueMV(nurseIndex,0);
	rpcSetValueMV(nurseIndex,0,(id+1));
	rpcReleaseLock(nurseIndexLock);
	
	if(debugFlag == true) {
		Printf("myDEBUG: Nurse [%d]: I entered!\n", id);
	}
	
	while(1){
		if(debugFlag == true) {
			Printf("myDEBUG:Nurse[%d]'s gonna to check waiting room\n", id);
		}
		/*check waiting Room if anyone waiting*/
		rpcAcquireLock(sittingRoomLock);
		
		if(rpcGetValueMV(sittingRoomCounter,0) == 0) {
			rpcReleaseLock(sittingRoomLock);
		}
		
		else{/*Some one is waiting*/
			/*Check Examination room state*/
			rpcAcquireLock(examStateLock);
			
			for(myERID=0;myERID<rpcGetValueMV(examRoomNum,0);myERID++){
				if(rpcGetValueMV(examState,myERID) == FREE){
					rpcSetValueMV(examState,myERID,BUSY);
					if(debugFlag == true) {
						Printf("myDEBUG:Nurse%d find examRoom %d FREE\n",id,myERID);
					}
					break;					
				}		
			}
			
			rpcReleaseLock(examStateLock);
			
			if(myERID < rpcGetValueMV(examRoomNum,0)){	/*Any empty exam room?*/
				rpcSetValueMV(sittingRoomCounter,0,rpcGetValueMV(sittingRoomCounter,0)-1);
				rpcReleaseLock(sittingRoomLock);
				rpcAcquireLock(waitingRoomLock);
				if(rpcGetValueMV(wrnState,0) == BUSY){
					rpcSetValueMV(waitingRoomNurseCounter,0,rpcGetValueMV(waitingRoomNurseCounter,0)+1);
					rpcWaitCV(waitingRoomLock,nurseWaitingWRCV);
				}
				
				else {
					rpcSetValueMV(wrnState,0,BUSY);
				}
				
				rpcAcquireLock(testLock);
				
				rpcReleaseLock(waitingRoomLock);
				rpcAcquireLock(wrnLock);
				rpcSetValueMV(wrnTask,0,CALLPATIENT);
				
				rpcSignalCV(wrnLock, wrnPatient_NurseCV);	/*wake up wrn, start interacting*/
				rpcWaitCV(wrnLock,wrnPatient_NurseCV);
				Printf("Nurse [%d] tells Waiting Room Nurse to give a new examination sheet.\n",id);
				rpcSetValueMV(nurseIDTOWRN,0,id);			
				rpcSignalCV(wrnLock, wrnPatient_NurseCV);
				rpcWaitCV(wrnLock,wrnPatient_NurseCV);
				
				rpcSetValueMV(wrnTask,0,WRONGTASK);
				
				rpcGetExamSheet(wrnExamSheet, &myExamSheet);
				if(debugFlag == true) {
					Printf("myDEBUG:Nurse[%d]get a patient with name %s%d\n", id, myExamSheet.name,myExamSheet.patientID);
				}
				rpcSignalCV(wrnLock, wrnPatient_NurseCV);
				
				/*prepare the data to interact with patient: nurseIDTOPatientSR*/
				rpcSetValueMV(nurseIDTOPatientSR,0,id);
				rpcSetValueMV(nurseSheetIDTOPatient,id,myExamSheet.patientID);
				
				/*wake patient up and let him wait for the nurse*/
				rpcAcquireLock(nurseLock[id]);
				rpcSignalCV(testLock,patientWaitingNurseSRCV);
				rpcReleaseLock(testLock);
				
				if(debugFlag == true) {
					Printf("myDEBUG:Nurse[%d] try to wake up a patient with name %s%d\n",id, myExamSheet.name,myExamSheet.patientID);
				}
				
				/*make sure wait nurseLock before p signal,get examsheet, 
				end interacting with wrn*/
				Printf("Nurse [%d] escorts Adult/Child Patient [%s%d] to the examination room [%d].\n", id, myExamSheet.name,myExamSheet.patientID,myERID);
				rpcSetValueMV(examIDTOPatient,id,myERID);
				
				rpcWaitCV(nurseLock[id],nursePatientCV[id]);
				rpcReleaseLock(wrnLock);
				rpcSignalCV(nurseLock[id],nursePatientCV[id]);
				
				/*interact with patient to do first exam*/	
				Printf("Nurse [%d] takes the temperature and blood pressure of Adult Patient/Parent [%s%d].\n", id, myExamSheet.name,myExamSheet.patientID);
				Printf("Nurse [%d] asks Adult Patient/Parent [%s%d] what Symptoms do you have?\n", id, myExamSheet.name,myExamSheet.patientID);
				Printf("Nurse [%d] writes all the information of Adult Patient/Parent [%s%d] in his examination sheet.\n", id, myExamSheet.name,myExamSheet.patientID);

				/*note right now patient need to get the ExamRoomLock*/
				rpcWaitCV(nurseLock[id],nursePatientCV[id]);
				if(debugFlag == true) {
					Printf("myDEBUG: Nurse [%d] finish Adult Patient/Parent [%s%d]\n",id, myExamSheet.name,myExamSheet.patientID);
				}
				rpcReleaseLock(nurseLock[id]);
				
				/*Now go and bring a doctor*/
				rpcAcquireLock(docStateLock);
				for(myDocID=0;myDocID<rpcGetValueMV(docNum,0);myDocID++) {
					if(rpcGetValueMV(docState,myDocID) == FREE){
						rpcSetValueMV(docState,myDocID,BUSY);
						break;
					}		
				}
				rpcReleaseLock(docStateLock);
				
				if(myDocID >= rpcGetValueMV(docNum,0)){
					rpcAcquireLock(docStateLock);
					rpcSetValueMV(docRoomCounter,0,rpcGetValueMV(docRoomCounter,0)+1);
					
					/*Append*/
					rpcSetValueMV(nurseIDLastItem,0,rpcGetValueMV(nurseIDLastItem,0)+1);
					if(rpcGetValueMV(nurseIDLastItem,0)==rpcGetValueMV(nurseIDListNum,0)){
						rpcSetValueMV(nurseIDLastItem,0,0);	
					}
					
					rpcSetValueMV(nurseIDList,rpcGetValueMV(nurseIDLastItem,0),id);	
					
					
					rpcWaitCV(docStateLock,nurseWaitingDocCV);
					myDocID = rpcGetValueMV(docIDTONurse,id);
					
					if(id!=rpcGetValueMV(nurseIDVerify,myDocID)){
						Printf("DEBUG FATAL ERROR: ERROR when pop and push nurse id in list\n");
					}
					
					rpcSignalCV(docStateLock,nurseWaitingDocCV);
					rpcReleaseLock(docStateLock);				
				}
				
				/*by now, we get a doctor with # myDocID*/
				/*we need to tell the doctor the exam room number and the patient id*/
				rpcAcquireLock(docLock[myDocID]);			
				rpcSetExamSheet(docExamSheet[myDocID], myExamSheet);
				rpcSetValueMV(examIDTODoctor,myDocID,myERID);
				Printf("Nurse [%d] informs Doctor [%d] that Adult/Child Patient [%s%d] is waiting in the examination room %d.\n", id, myDocID, myExamSheet.name,myExamSheet.patientID,myERID);
				Printf("Nurse [%d] hands over to the Doctor [%d] the examination sheet of Adult/Child Patient [%s%d].\n", id, myDocID, myExamSheet.name,myExamSheet.patientID);
				rpcSignalCV(docLock[myDocID],nurseDocCV[myDocID]);
				rpcWaitCV(docLock[myDocID],nurseDocCV[myDocID]);
				rpcReleaseLock(docLock[myDocID]);
				
				Printf("DEBUG: Nurse [%d] finish with Doctor [%d] \n", id, myDocID);
			}
			
			else{
				rpcReleaseLock(sittingRoomLock);
			}	
		}	/*End of else someone waiting*/
		
		nurseTaskDelay();
		
		
		/*check Examination Room wall pocket*/
		for(myWallPocketERID=0;myWallPocketERID<rpcGetValueMV(examRoomNum,0);myWallPocketERID++){
			rpcAcquireLock(examRoomLock[myWallPocketERID]);	
			
			if(rpcGetStatus(wallPocketER[myWallPocketERID])!=EMPTY){
				rpcGetExamSheet(wallPocketER[myWallPocketERID],&myExamSheet);
				if(debugFlag == true) {
					Printf("myDEBUG:Nurse[%d] find wall pocket exam [%d] filled.\n", id, myWallPocketERID);
				}
				rpcSetStatus(wallPocketER[myWallPocketERID], EMPTY);
				rpcReleaseLock(examRoomLock[myWallPocketERID]);
				break;
			}
			rpcReleaseLock(examRoomLock[myWallPocketERID]);		
		}
		
		if(myWallPocketERID<rpcGetValueMV(examRoomNum,0)){/*if any wall pocket, get inside and take patient to shot or Xray*/
			if(debugFlag == true) {
				Printf("myDEBUG:Nurse->nextDestination %d\n",myExamSheet.nextDestination);
			}
			
			if(myExamSheet.nextDestination == EXAMROOM){	/*take patient to shot*/
				rpcAcquireLock(cabinetLock);
				if(rpcGetValueMV(cabinetState,0) == BUSY){
					rpcSetValueMV(cabinetCounter,0,rpcGetValueMV(cabinetCounter,0)+1);
					rpcWaitCV(cabinetLock,nurseWaitingcabinetCV);
					if(debugFlag == true) {
						Printf("myDEBUG: nurse [%d] waiting for cabinet Lock.\n", id);
					}
				}
				else {
					rpcSetValueMV(cabinetState,0,BUSY);
				}
				rpcReleaseLock(cabinetLock);
				/*by here, we need to get into the cabinet*/
				Printf("Nurse [%d] goes to supply cabinet to give to take medicine for Adult/Child Patient [%s%d].\n", id, myExamSheet.name,myExamSheet.patientID);
				rpcAcquireLock(cabinetLock);
				if(rpcGetValueMV(cabinetCounter,0) > 0){
					rpcSetValueMV(cabinetCounter,0,rpcGetValueMV(cabinetCounter,0)-1);
					rpcSignalCV(cabinetLock, nurseWaitingcabinetCV);
					if(debugFlag == true) {
						Printf("myDEBUG: Nurse [%d] Signal the cabinetCV", id);
					}			
				}
				else{
					rpcSetValueMV(cabinetState,0,FREE);
				}
				rpcReleaseLock(cabinetLock);
				/*by here, we got the medicine, and we need to give shot to patient*/
				rpcAcquireLock(examRoomLock[myWallPocketERID]);
				rpcSetValueMV(nurseIDTOPatientER,myWallPocketERID,id); /*prepare the data to interaction with patient which examroom to go*/
				
				rpcAcquireLock(nurseLock[id]);
				Printf("Nurse [%d] asks Adult/Child Patient [%s%d] \"Are you ready for the shot?\"\n", id, myExamSheet.name,myExamSheet.patientID);
				rpcSignalCV(examRoomLock[myWallPocketERID], patientWaitingNurseERCV[myWallPocketERID]);
	
				rpcReleaseLock(examRoomLock[myWallPocketERID]);
				rpcWaitCV(nurseLock[id],nursePatientCV[id]);
				Printf("Nurse [%d] tells Adult/Child Patient [%s%d] \"Your shot is over.\"\n", id, myExamSheet.name,myExamSheet.patientID);
				
				if(myExamSheet.needXray == true){
					if(debugFlag == true) {
						Printf("myDEBUG:Nurse[%d] find patient need Xray\n", id);
					}
					myExamSheet.nextDestination = XRAYROOM;	
				}
				else {
					myExamSheet.nextDestination = CASHIER;
				}
				rpcReleaseLock(nurseLock[id]);
				
				if(debugFlag == true) {
					Printf("myDEBUG:nextDestination:%d",myExamSheet.nextDestination);
					Printf("myDEBUG:Nurse[%d] have given shot to patient[%d], and released my own nurse lock\n",id, myExamSheet.patientID);
				}
			}/*end of if patient take shot*/
			if(myExamSheet.nextDestination == XRAYROOM){	/*need go to Xray*/
				if(debugFlag == true) {
					Printf("myDEBUG:Nurse[%d] is checking xray state\n", id);
				}
				rpcAcquireLock(xrayStateLock);
				for(myXRID=0;myXRID<rpcGetValueMV(xrayRoomNum,0);myXRID++){
					if(debugFlag == true) {
						Printf("myDEBUG:Nurse[%d] is checking xray state %d\n", id, myXRID);
					}
					if(rpcGetValueMV(xrayState,myXRID) == FREE){
						rpcSetValueMV(xrayState,myXRID,BUSY);
						if(debugFlag == true) {
							Printf("myDEBUG:Nurse[%d] find empty xray room\n", id);
						}
						rpcAcquireLock(xrayRoomLock[myXRID]);
						rpcSetExamSheet(xrayExamSheet[myXRID],myExamSheet);
						rpcReleaseLock(xrayRoomLock[myXRID]);
						break;					
					}				
				}
				rpcReleaseLock(xrayStateLock);
				
				if(myXRID<xrayRoomNum){	/*have empty xray room, go to xray*/
					rpcAcquireLock(examRoomLock[myWallPocketERID]);
					rpcSetValueMV(xrayIDTOPatient,myWallPocketERID,myXRID);
					
					Printf("Nurse [%d] escorts %s%d to the X-ray room [%d].\n", id, myExamSheet.name,myExamSheet.patientID,myXRID);
					rpcSignalCV(examRoomLock[myWallPocketERID], patientWaitingNurseERCV[myWallPocketERID]);
					rpcReleaseLock(examRoomLock[myWallPocketERID]);			
				}
				else{	/*no empty room*/
					rpcAcquireLock(examRoomLock[myWallPocketERID]);
					rpcSetExamSheet(wallPocketER[myWallPocketERID],myExamSheet);
					rpcSetStatus(wallPocketER[myWallPocketERID],USED);
					rpcReleaseLock(examRoomLock[myWallPocketERID]);	
				}			
			}/*end of if go to next goto xray*/
			
			if(myExamSheet.nextDestination == CASHIER){
				rpcAcquireLock(examRoomLock[myWallPocketERID]);
				Printf("Nurse [%d] escorts %s%d to Cashier.\n",id, myExamSheet.name,myExamSheet.patientID);
				rpcSetValueMV(verifyPatientID,myWallPocketERID,myExamSheet.patientID);
				rpcSignalCV(examRoomLock[myWallPocketERID], patientWaitingNurseERCV[myWallPocketERID]);
				rpcReleaseLock(examRoomLock[myWallPocketERID]);
			}/*end of if need to go to cashier.*/		
		}	/*end of if any wall pocket*/
		
		nurseTaskDelay();
		
		/*Printf("DEBUG:Nurse[%d]'s gonna to check xray room\n", id);*/
		/*check Xray Room wall pocket*/
		
		Printf("DEbug: the xrayRoomNum is %d\n" ,rpcGetValueMV(xrayRoomNum,0));
		for(myWallPocketXRID=0;myWallPocketXRID<rpcGetValueMV(xrayRoomNum,0);myWallPocketXRID++){
			rpcAcquireLock(xrayRoomLock[myWallPocketXRID]);
			
			if(rpcGetStatus(wallPocketXR[myWallPocketXRID])!= EMPTY){
				rpcGetExamSheet(wallPocketXR[myWallPocketXRID], &myExamSheet);
				rpcSetStatus(wallPocketXR[myWallPocketXRID],EMPTY);
				if(debugFlag == true) {
					Printf("myDEBUG:Nurse [%d] find this myWallPocketXRID is not empty", myWallPocketXRID);
					Printf("myDEBUG:Nurse [%d] find xray wall pocket with name %s%d",id, myExamSheet.name,myExamSheet.patientID);
				}
				rpcSetValueMV(nurseIDTOPatientXR,myWallPocketXRID,id);	/*prepare the data to interact with patient*/
				rpcReleaseLock(xrayRoomLock[myWallPocketXRID]);
				break;
			}
			rpcReleaseLock(xrayRoomLock[myWallPocketXRID]);
		}
		
		if(myWallPocketXRID<rpcGetValueMV(xrayRoomNum,0)){
			/*check if any empty examination room*/
			rpcAcquireLock(examStateLock);
			for(myERID=0;myERID<rpcGetValueMV(examRoomNum,0);myERID++){
				if(rpcGetValueMV(examState,myERID) == FREE){
					rpcSetValueMV(examState,myERID,BUSY);
					rpcAcquireLock(examRoomLock[myERID]);
					break;
				}
			}
			rpcReleaseLock(examStateLock);
			
			if(myERID<rpcGetValueMV(examRoomNum,0)){/*if any empty examination room*/
				rpcAcquireLock(xrayRoomLock[myWallPocketXRID]);
				rpcAcquireLock(nurseLock[id]);
				rpcSetValueMV(examIDTOPatient,id,myERID);/*prepare the data to interact with patient*/
				rpcReleaseLock(examRoomLock[myERID]);
				Printf("Nurse [%d] escorts %s%d to the examination room [%d].\n", id, myExamSheet.name,myExamSheet.patientID,myERID);
				
				rpcSignalCV(xrayRoomLock[myWallPocketXRID],patientWaitingNurseXRCV[myWallPocketXRID]);
				rpcReleaseLock(xrayRoomLock[myWallPocketXRID]);
				rpcWaitCV(nurseLock[id],nursePatientCV[id]);
				rpcReleaseLock(nurseLock[id]);
				
				/*nurse go to get the doctor*/
				rpcAcquireLock(docStateLock);
				
				for(myDocID=0;myDocID<rpcGetValueMV(docNum,0);myDocID++){
					if(rpcGetValueMV(docState,myDocID) == FREE) {
						rpcSetValueMV(docState,myDocID,BUSY);
						break;
					}			
				}
				rpcReleaseLock(docStateLock);
				
				if(myDocID >= rpcGetValueMV(docNum,0)){/*no doctor free, wait*/
					rpcAcquireLock(docStateLock);				
					rpcSetValueMV(docRoomCounter,0,rpcGetValueMV(docRoomCounter,0)+1);
					
					/*Append*/
					rpcSetValueMV(nurseIDLastItem,0,rpcGetValueMV(nurseIDLastItem,0)+1);
					if(rpcGetValueMV(nurseIDLastItem,0)==rpcGetValueMV(nurseIDListNum,0)) {
						rpcSetValueMV(nurseIDLastItem,0,0);
					}				
					rpcSetValueMV(nurseIDList,rpcGetValueMV(nurseIDLastItem,0),id);
					
					rpcWaitCV(docStateLock,nurseWaitingDocCV);
					myDocID = rpcGetValueMV(docIDTONurse,id);/*Doctor tell the nurse his ID*/
					
					if(id!=rpcGetValueMV(nurseIDVerify,myDocID)){
						Printf("DEBUG FATAL ERROR: ERROR when pop and push nurse id in list\n");
					}
					rpcSignalCV(docStateLock,nurseWaitingDocCV);
					rpcReleaseLock(docStateLock);				
				}
				
				/*by now, we get a doctor myDOCID*/
				rpcAcquireLock(docLock[myDocID]);
				rpcSetExamSheet(docExamSheet[myDocID], myExamSheet);
				rpcSetValueMV(examIDTODoctor,myDocID,myERID);
				Printf("Nurse [%d] informs Doctor [%d] that Adult/Child Patient [%s%d] is waiting in the examination room %d.\n", id, myDocID, myExamSheet.name,myERID,myExamSheet.patientID);
				Printf("Nurse [%d] hands over to the Doctor [%d] the examination sheet of Adult/Child Patient [%s%d].\n", id, myDocID, myExamSheet.name,myExamSheet.patientID);
				rpcSignalCV(docLock[myDocID],nurseDocCV[myDocID]);
				rpcWaitCV(docLock[myDocID],nurseDocCV[myDocID]);
				rpcReleaseLock(docLock[myDocID]);		
			}
				
			else{	/*if no empty exam room*/
				rpcAcquireLock(xrayRoomLock[myWallPocketXRID]);
				rpcSetExamSheet(wallPocketXR[myWallPocketXRID],myExamSheet);
				rpcReleaseLock(xrayRoomLock[myWallPocketXRID]);
			}	/*end if no empty room*/
				
		}
		
		nurseTaskDelay();
/*	
		rpcAcquireLock(patientLeftNumLock);	

		if(rpcGetValueMV(patientLeftNum,0) == rpcGetValueMV(patientNum,0)) {
			Exit(0);
		}
		rpcReleaseLock(patientLeftNumLock);*/
	}/*end-while*/
}